from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db import models
from django.db import *

from inv.utils.trace   import *
from inv.utils.errors  import *
from inv.admin.models  import *
from inv.utils.misc    import *

from inv.settings import *

from datetime import datetime
import csv, os

"""This is for pydoc to catch"""

def Meta_Table(met_type) :
   Meta_Table_Dict = {
      'INT'       : Int,
      'FLOAT'     : Float,
      'CHAR'      : Char,
      'CHAR_UNIQ' : Char_Uniq,
      'TEXT'      : Text,
      'COMBO'     : Combo
   }
   return Meta_Table_Dict[met_type]



class Object(models.Model):
   """Writing my comments here
   """
   cls_id = models.IntegerField()


#
#  This function returns the meta id list and meta type list
#  for a list of meta names
#
#  Parameter  :
#     met_names : List of meta names
#
#  Return Value :
#     List of meta ids
#     List of meta types
#
#  Exceptions :
#     Meta name does not exists
#        AssertionError(1, <<Meta name>>) 
#
#     Meta name provided twice
#        AssertionError(2, <<Meta name>>
#
   def extract_meta_details(self, met_names) :
      """
      My function here
      """
      met_ids = []
      met_types = []
      ENT.debug("Fetching details for Meta name List = " + L(met_names),
         extra={'cls':self})
      try : 
         for met_name in met_names :
            met_name = refine_col_val("CHAR", met_name)
            met = Class_Meta.objects.get(cls_id=self.cls_id, nam=met_name)
            if met_ids.count(met.id) > 0 : 
               raise AssertionError(2, met_name)
            met_ids.append(met.id)
            met_types.append(met.typ)
         ENT.debug("Meta Id list = " + L(met_ids) + 
            " Meta Type List = " + L(met_types), extra={'cls':self})
      except Class_Meta.DoesNotExist, (detail) :
         raise AssertionError(1, met_name)
      return met_ids, met_types


   @transaction.commit_manually
   def add_internal(self, met_ids, met_types, vals) :
      ret = err[5][0]
      self.save()
      ENT.debug("Attempt to add Meta Ids = " + L(met_ids) 
         + " Meta Types = " + L(met_types) + " Values = " + L(vals),
         extra={'cls':self})
      try :
         for i in range(0, len(met_ids)) :
            if (met_types[i] != 'COMBO') :
               val = refine_col_val(met_types[i], vals[i])
               if val != None : 
                  Meta_Table(met_types[i])(cls_id=self.cls_id, met_id=met_ids[i],
                     obj_id=self.id, val=val).save()
            else :
               val = refine_col_val(met_types[i], vals[i], self.cls_id, met_ids[i])
               if val != None : 
                  Meta_Table(met_types[i])(cls_id=self.cls_id, met_id=met_ids[i],
                     obj_id=self.id, com_id=val.id).save()
      except ValueError, (detail) :
         met = Class_Meta.objects.get(cls_id=self.cls_id, id=met_ids[i])
         ret = err[5][4].copy()
         ret['rm'] = ret['rm'] + met.nam
      except IntegrityError :
         met = Class_Meta.objects.get(cls_id=self.cls_id, id=met_ids[i])
         ret = err[5][5].copy()
         ret['rm'] = ret['rm'] + met.nam
      finally :
         if ret['rc'] == 0 :
            transaction.commit()
         else :
            transaction.rollback()
      return ret

   def check_dependency(self):
      Class.objects.get(id=self.cls_id)

   def add(self, met_names, vals) :
      ret = err[5][0]
      ENT.debug("Attempt to add Meta Names = " + L(met_names) 
         + " Values = " + L(vals), extra={'cls':self})
      try : 
         self.check_dependency()
         met_ids, met_types = self.extract_meta_details(met_names)
         ret = self.add_internal(met_ids, met_types, vals)
      except AssertionError, (errno, met_name):
         ret = err[5][errno].copy()
         ret['rm'] = ret['rm'] + met_name
      except Class.DoesNotExist, (detail) :
         ret = err[5][3]
      return ret


class Int(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   val = models.IntegerField(null=True)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'),)
    
class Float(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   val = models.FloatField(null=True)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'),)
      
class Char(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   val = models.CharField(max_length=255, null=True)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'),)

class Text(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   val = models.TextField(null=True)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'),)

class Char_Uniq(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   val = models.CharField(max_length=255, null=True)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'), ('cls_id', 'met_id', 'val'),)

class Combo(models.Model):
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   obj_id = models.IntegerField()
   com_id = models.IntegerField()
   class Meta:
      unique_together = (('cls_id', 'met_id', 'obj_id'),)

class Combo_Content(models.Model) :
   cls_id = models.IntegerField()
   met_id = models.IntegerField()
   val = models.CharField(max_length=255)
   class Meta:
      unique_together = (('cls_id', 'met_id', 'val'),)

   def trace(self):
      return "[" + "class_id = " + str(self.cls_id) \
         + ", meta_id = " + str(self.met_id) \
         + ", value = " + str(self.val) + "]"

   def refine(self):
      self.val = refine_col_val("CHAR", self.val)

   def check_dependency(self):
      Class.objects.get(id=self.cls_id)
      met = Class_Meta.objects.get(id=self.met_id)

      if met.typ != "COMBO" :
         raise AssertionError
      
   def add(self) :
      ret = err[4][0]
      try :
         self.check_dependency()

         self.refine()

         if self.val == None :
            ret = err[4][1]
         else :
            self.save()

      except IntegrityError :
         ret = err[4][2]
      except Class.DoesNotExist, (detail) :
         ret = err[4][3]
      except Class_Meta.DoesNotExist, (detail) :
         ret = err[4][4]
      except AssertionError :
         ret = err[4][5]
      ENT.debug(self.trace() + " " + ret['rm'], extra={'cls':self})
      return ret

class Bulk_Add(models.Model):
   cls_id = models.IntegerField()
   fname = models.CharField(max_length=255)
   due_date = models.DateTimeField()
   desc = models.TextField(null=True)
   abs_fname = models.CharField(max_length=255)
   state = models.IntegerField()
#  1-Pending, 2-In Progress, 3-Added, 4-Cancelled

   def trace(self):
      return "[" + "cls_id = " + str(self.cls_id) \
         + ", filename = " + str(self.fname) \
         + ", absolute filename = " + str(self.abs_fname) \
         + ", due_date = " + str(self.due_date) \
         + ", state = " + str(self.state) \
         + ", desc = " + str(self.desc) + "]"

   def check_dependency(self):
      Class.objects.get(id=self.cls_id)

   def refine(self):
      self.due_date = refine_col_val("DATE", self.due_date)
      if self.due_date == None or self.due_date < datetime.now() :
         raise ValueError ("Invalid Date Time")
      self.desc = refine_col_val("TEXT", self.desc)
      self.fname = refine_col_val("CHAR", self.fname)
      self.abs_fname = INV_UPLOADED_CSV_DIR + self.fname[:-4] + '.' + now_str() + self.fname[-4:]
      self.state = 1

   def add(self, file) :
      ret = err[6][0]
      try : 
         self.check_dependency()
         self.refine()
         ENT.debug(self.trace(), extra={'cls':self})
         self.save_file(file)
      except ValueError, (detail) :
         ret = err[6][5]
      except Class.DoesNotExist, (detail) :
         ret = err[6][3]
      except AssertionError, (errno, met_name) :
         os.remove(self.abs_fname)
         ret = err[6][errno].copy()
         ret['rm'] = ret['rm'] + met_name
      return ret

   def save_file(self, file) : 
      if self.abs_fname[-4:] != ".CSV" :
         raise AssertionError(4, "Invalid file type")
      destination = open(self.abs_fname, 'wb+')
      for chunk in file.chunks():
         destination.write(chunk)
      destination.close()
      reader = csv.reader(open(self.abs_fname, "rU"))
      for row in reader :
         Object(cls_id=self.cls_id).extract_meta_details(row)
         break
